﻿

using System;
using System.Text;
using FacebookXmpp.Parse;
using FacebookXmpp.Stanzas;

namespace FacebookXmpp
{
	internal class StanzaStream : IDisposable
    {
    	private readonly Dashboard _dashboard;
        private readonly Connection _connection;
		private readonly XmlStream _xmlStream;
        private readonly StanzaCollection _collection;
        public bool Connected { get { return _connection != null && _connection.Connected; } }

#if DEBUG
		public StringBuilder Debug { get { return _xmlStream.Debug; } }
#endif

    	public StanzaStream(Dashboard dashboard)
    	{
            _dashboard = dashboard;
            _dashboard.WorkerState("Connecting");
            _connection = new Connection();
            if (!_connection.Connected)
            {
                _dashboard.WorkerState("Failed to connect");
            }
            else
            {
                _collection = new StanzaCollection();
                _xmlStream = new XmlStream(_connection);
            }
    	}
        
        public void Dispose()
        {
            // XmlStream disposes the _connection
            _xmlStream.Dispose();
        }

        public Dashboard Dashboard { get { return _dashboard; } }
		
        internal void Process()
    	{
            while (_collection.Any())
			{
                // Send out messages from the handlers
				foreach (var message in _collection.CallEach( hdl => hdl.MessageToService() ))
					_connection.Send( (string) message );

				// Process is completed when there are no handlers
				if (!_collection.Any())
					break;
				
                // Get a response from the service
				Expectation responseExpectation = _collection.HighestExpectation();
				
                // Handlers aren't interested in a response
				if (responseExpectation == Expectation.None)
					break;

				// If response expectation is HIGH then 
				//     wait for TimeOut, by waiting breaking it into 100 iteration pieces
				// else
				//     wait for atleast 5 x 100 milliseconds

				int retryCount = (responseExpectation == Expectation.High)
				                 	? Dashboard.TimeoutMilliseconds/100
				                 	: 5;

				MessageData recieved = null;
				bool allowOpen = _collection.AnyAllowOpen;
				
				while (recieved == null)
				{
					if (--retryCount < 0)
                        return;
            
					recieved = _xmlStream.ConsumeNext( allowOpen );

					if (recieved == null && retryCount > 0)
                        System.Threading.Thread.Sleep(100);
				}
                
				// Let all the handlers see the response
				_collection.ProcessResponse( recieved );
			}
    	}

		internal void InjectStream(MessageData data)
		{
			_xmlStream.Sequence.PushBack( data );
		}

		internal void StartTls( )
		{
			_connection.StartTls();
		}

		internal void AddHandler( Stanza stanza )
		{
			stanza.SetStream( this );
			_collection.Add( stanza );
		}

		internal void AddHandler( Stanza stanza, StanzaListenCriteria listenFor )
		{
			stanza.SetStream( this );
			_collection.Add( stanza, listenFor );
		}

		internal void RemoveHandler( Stanza stanza )
		{
			_collection.Remove( stanza );
		}

		internal void ClearHandlers()
		{
			_collection.Clear();
		}
    }
}